#include <stdx/all.h>

const char* GetTemplateConfig();

#ifndef XG_LINUX
const char* GetResourceTemplate();
SmartBuffer GetApplicationIconData();
#endif

class CppShellApplication : public Application
{
protected:
	string cmd;
	string path;
	string appname;
	string exepath;
	string reslink;
	time_t utime = 0;
	time_t etime = 0;
	time_t rtime = 0;
	const char* srcpath = NULL;

	string checkResource()
	{
#ifndef XG_LINUX
		const char* end = strstr(srcpath, ".");

		if (end)
		{
			string content;
			string respath = string(srcpath, end) + ".rc";

			rtime = path::mtime(respath);

			if (rtime <= 0)
			{
				content = GetResourceTemplate();

				rtime = etime;
			}
			else
			{
				stdx::GetFileContent(content, respath);
			}

			string reslink = respath + ".o";

			if (rtime >= etime || path::type(reslink) == eNONE)
			{
				XFile file;
				string icon = appname + ".ico";
				string data = stdx::replace(content, "${ICON}", icon);

				if (data != content)
				{
					SmartBuffer tmp = GetApplicationIconData();

					file.open(icon, eCREATE);
					file.write(tmp.str(), tmp.size());
					file.close();
				}

				data = stdx::replace(data, "${NAME}", appname);
				data = stdx::replace(data, "${VERSION}", "1.0.0");
				data = stdx::replace(data, "${COPYRIGHT}", "copyright@winfeng");

				if (data != content)
				{
					file.open(respath, eCREATE);
					file.write(data.c_str(), data.size());
					file.close();
				}

				proc::exec("windres", respath, reslink);

				rtime = etime;
			}

			return reslink;
		}
#endif
		return stdx::EmptyString();
	}
	bool loadConifg()
	{
		path = path::parent(srcpath = GetCmdParam(1));

		if (path.empty())
		{
			path = Process::GetCurrentDirectory();
			if (path.empty()) path = ".";
			appname = srcpath;
		}
		else
		{
			static string filename = appname = path::name(srcpath);
			Process::SetCurrentDirectory(path);
			srcpath = filename.c_str();
		}

		appname = appname.substr(0, appname.rfind('.'));
		exepath = path + "/.bin/" + appname;

#ifndef XG_LINUX
		exepath += ".exe";
#endif
		etime = path::mtime(exepath);
		utime = path::mtime(srcpath);
		reslink = checkResource();

		if (path::size("config.yml") > 0)
		{
			if (etime > utime && etime > rtime && etime > path::mtime("config.yml")) return true;

			CHECK_FALSE_RETURN(yaml::open("config.yml"));
		}
		else
		{
			if (etime > utime && etime > rtime) return true;

			if (proc::exec("which", "g++").str())
			{
				yaml::set("cc", "g++");
			}
			else
			{
				yaml::set("cc", "gcc");
			}
		}

		string cc = yaml::raw("cc");
		string flag = yaml::raw("flag");
		string link = yaml::raw("link");
		
		auto translate = [](const string& str){
			string res;
			vector<string> vec = stdx::split(stdx::replace(str, "\t", " "), " ");

			for (string& item : vec)
			{
				item = stdx::trim(item);

				if (item.empty()) continue;

				res += " " + stdx::quote(stdx::translate(item));
			}

			res = stdx::replace(res, "\t", " ");
			res = stdx::replace(res, "  ", " ");

			return stdx::trim(res);
		};

		auto checklink = [&](const string& lib){
			string tag = " " + lib + " ";
			string tmp = " " + link + " ";
			if (tmp.find(tag) == string::npos) link += " " + lib;
		};

		auto checkflag = [&](const string& lib){
			string tag = " " + lib + " ";
			string tmp = " " + flag + " ";
			if (tmp.find(tag) == string::npos) flag += " " + lib;
		};

		cc = translate(cc);
		flag = translate(flag);
		link = translate(link);

		checkflag("-pthread");

		if (reslink.length() > 0) checklink(reslink);

#ifdef XG_LINUX
		checkflag("-DXG_LINUX");
		checklink("-lutil");
		checklink("-ldl");
		checklink("-lm");
#else
		flag = stdx::replace(flag, "-DXG_LINUX", "");

		if (GetCmdParam("--{hidden}")) checkflag("-mwindows");

		checklink("-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,-Bdynamic,--no-whole-archive");
		checklink("-static-libstdc++ -static-libgcc");
		checklink("-lcomdlg32");
		checklink("-lgdiplus");
		checklink("-lws2_32");
		checklink("-lole32");
		checklink("-lgdi32");
		checklink("-lpsapi");
		checklink("-luuid");
		checklink("-lm");
#endif
		stdx::format(cmd, "%s %s -o %s %s %s", cc.c_str(), flag.c_str(), stdx::quote(exepath).c_str(), srcpath, link.c_str());

		path::mkdir(".bin");

		return true;
	}

public:
	bool main()
	{
		string msg;
		const char* param = NULL;
		int cnt = GetCmdParamCount();
		static char buffer[40960] = { 0 };

		if ((param = GetCmdParam("-etc")))
		{
			if (*param)
			{
				TextFile out;
				
				if (GetCmdParam("-i") && path::size(param) > 0)
				{
					CHECK_FALSE_RETURN(cmdx::CheckCommand("file[%s] exists, overwrite or not ? (y/n)", path::name(param).c_str()));
				}

				if (out.open(param, true))
				{
					out << GetTemplateConfig();

					puts("export template config success");
				}
				else
				{
					puts("export template config failed");

					return false;
				}
			}
			else
			{
				puts(GetTemplateConfig());
			}

			return true;
		}
		
		if (GetCmdParam(1) == NULL)
		{
			ColorPrint(eRED, "%s\n", "please input source file");

			return false;
		}

		if (path::size(GetCmdParam(1)) < 0)
		{
			ColorPrint(eRED, "%s\n", "load source file failed");

			return false;
		}
		
		loadConifg();

		if (exepath.empty())
		{
			ColorPrint(eRED, "%s\n", "load build-essential failed");

			return false;
		}

		if (cmd.length() > 0)
		{
			if (cmdx::RunCommand(cmd, buffer, sizeof(buffer)) > 0) msg = buffer;

			if (path::mtime(exepath) < utime)
			{
				if (msg.empty()) msg = "compile failed";

				ColorPrint(eRED, "%s\n", msg.c_str());

				return false;
			}
		}

		if (GetCmdParam("--{compile}"))
		{
			ColorPrint(eGREEN, "%s\n", "compile success");

			return true;
		}

		cmd = stdx::quote(exepath);

		for (int i = 2; i < cnt; i++)
		{
			const char* param = GetCmdParam(i);

			if (strcmp(param, "--{hidden}"))
			{
				cmd += " " + stdx::quote(param);
			}
		}

		return System(cmd.c_str()) >= 0;
	}
};

START_APP(CppShellApplication)